package lottery

import (
	"errors"
	"fmt"
	"math"
	"time"

	"lottery/constant"
	"lottery/models"
	"lottery/utils"

	"code.google.com/p/go.net/context"
	"github.com/astaxie/beego/logs"
)

func (u *UserProc) playProc() {
	for playCells := range u.playCellChan {
		for _, playCell := range playCells {
			u.triggerProc(playCell.Policy, playCell.Order,
				playCell.GameAgentId, playCell.GameData,
				playCell.OtherGameAgentId, playCell.OtherGameData)
		}
	}
}

func (u *UserProc) triggerProc(
	policy *models.Policy, orderBy bool,
	gameAgentId int64, gameData *models.GameData,
	otherGameAgentId int64, otherGameData *models.GameData) (success bool) {

	startProcTime := utils.CurrentMills()

	gameNewRate := gameData.HRate
	otherGameNewRate := otherGameData.ARate
	gameDataTeam := 1
	otherGameDataTeam := 2
	if !orderBy {
		gameDataTeam = 2
		otherGameDataTeam = 1
		// 水位
		gameNewRate = gameData.ARate
		otherGameNewRate = otherGameData.HRate
	}

	var timeOutLimit int64 = 200

	// 保证休眠，取消准备下单状态
	defer func(gameAgentId int64, gamePlatformId int, otherGameAgentId int64, otherGamePlatformId int) {
		if gameAgentId > 0 {
			u.setLotterySleep(gameAgentId, gamePlatformId)
		}

		if otherGameAgentId > 0 {
			u.setLotterySleep(otherGameAgentId, otherGamePlatformId)
		}
	}(gameAgentId, gameData.PlatformId, otherGameAgentId, otherGameData.PlatformId)

	////////////////////////////////////

	// now := utils.CurrentMills()
	// logs.Notice("平台 %d 账号 %d 处理时长 %d ms, 平台 %d 账号 %d 处理时长 %d ms", gameData.PlatformId, gameData.AgentId, now-gameData.CreateTime, otherGameData.PlatformId, otherGameData.AgentId, now-otherGameData.CreateTime)

	var (
		err               error
		discard           bool
		gameTickData      interface{}
		otherGameTickData interface{}
	)

	// 获取点水下单账号
	gameLottery, ok := u.getPlaceLottery(gameAgentId, gameData.PlatformId)
	if gameLottery == nil || !ok {
		logs.Warn("平台 %d 获取打水账号 %d 失败", gameData.PlatformId, gameData.GetAgentId())
		return
	}

	defer func() {
		u.putPlaceLottery(gameLottery)
	}()

	otherGameLottery, ok := u.getPlaceLottery(otherGameAgentId, otherGameData.PlatformId)
	if otherGameLottery == nil || !ok {
		logs.Warn("平台 %d 获取打水账号 %d 失败", otherGameData.PlatformId, otherGameData.GetAgentId())
		return
	}

	defer func() {
		u.putPlaceLottery(otherGameLottery)
	}()

	if gameData.PlatformId == constant.U16888 {
		if !u.fillU16888HandicapId(gameLottery.AgentInfo(), gameData) {
			logs.Warn("平台 %d 获取打水账号 %d, 填充下注ID失败", gameData.PlatformId, gameLottery.AgentInfo().Id)
			return
		}
	}

	if gameData.GameTickData == nil {
		startTickTime := utils.CurrentMills()
		gameNewRate, gameTickData, discard, err = gameLottery.Tick(gameDataTeam, gameData)
		if err != nil || discard {
			var msg string
			if err != nil {
				msg = err.Error()
			} else {
				msg = "放弃水位"
			}
			logs.Warn("平台 %d 获取点水失败 无法下单 %d %+v %s", gameLottery.Platform(), gameDataTeam, gameData, msg)
			return
		}

		if utils.CurrentMills()-startTickTime > timeOutLimit {
			logs.Warn("平台 %d 获取点水超时 无法下单 %d", gameLottery.Platform(), gameDataTeam)
			return
		}
	} else {
		gameTickData = gameData.GameTickData
	}

	// ===========

	if otherGameData.PlatformId == constant.U16888 {
		if !u.fillU16888HandicapId(otherGameLottery.AgentInfo(), otherGameData) {
			logs.Warn("平台 %d 获取打水账号 %d, 填充下注ID失败", otherGameData.PlatformId, otherGameLottery.AgentInfo().Id)
			return
		}
	}

	if otherGameData.GameTickData == nil {
		startTickTime := utils.CurrentMills()
		otherGameNewRate, otherGameTickData, discard, err = otherGameLottery.Tick(otherGameDataTeam, otherGameData)
		if err != nil || discard {
			var msg string
			if err != nil {
				msg = err.Error()
			} else {
				msg = "放弃水位"
			}

			logs.Warn("平台 %d 获取点水失败 无法下单 %d %+v %s", otherGameLottery.Platform(), otherGameDataTeam, otherGameData, msg)
			return
		}

		if utils.CurrentMills()-startTickTime > timeOutLimit {
			logs.Warn("平台 %d 获取点水超时 无法下单 %d", otherGameLottery.Platform(), otherGameDataTeam)
			return
		}
	} else {
		otherGameTickData = otherGameData.GameTickData
	}

	var rowMergeRate float64
	var nowMergeRate float64 = gameNewRate + otherGameNewRate
	if gameDataTeam == 1 {
		rowMergeRate = gameData.HRate + otherGameData.ARate
	} else {
		rowMergeRate = gameData.ARate + otherGameData.HRate
	}

	if gameData.HScore != otherGameData.HScore || gameData.AScore != otherGameData.AScore {
		logs.Warn("比分不一致,取消下单 平台:%d 比分:%d:%d 平台:%d 比分:%d:%d",
			gameData.PlatformId, gameData.HScore, gameData.AScore,
			otherGameData.PlatformId, otherGameData.HScore, otherGameData.AScore)
		return
	}

	now := utils.CurrentMills()
	if !u.policyRule(otherGameNewRate, gameNewRate) {
		logs.Warn("盘口:%0.2f 类型:%d 点水后水位不符合 原合水:%0.2f 现合水:%0.2f 平台 %d 账号 %d team %d 处理时长 %d ms, 平台 %d 账号 %d team %d 处理时长 %d ms",
			gameData.Handicap,
			gameData.HandicapType,
			rowMergeRate,
			nowMergeRate,
			gameData.PlatformId,
			gameData.AgentId,
			gameDataTeam,
			now-gameData.CreateTime,
			otherGameData.PlatformId,
			otherGameData.AgentId,
			otherGameDataTeam,
			now-otherGameData.CreateTime)
		return
	} else {
		logs.Notice("盘口:%0.2f 类型:%d 点水后水位符合 原合水:%0.2f 现合水:%0.2f 平台 %d 账号 %d team %d 处理时长 %d ms, 平台 %d 账号 %d team %d 处理时长 %d ms",
			gameData.Handicap,
			gameData.HandicapType,
			rowMergeRate,
			nowMergeRate,
			gameData.PlatformId,
			gameData.AgentId,
			gameDataTeam,
			now-gameData.CreateTime,
			otherGameData.PlatformId,
			otherGameData.AgentId,
			otherGameDataTeam,
			now-otherGameData.CreateTime)
	}

	// 保存水位 更新点水后的水位
	if gameDataTeam == 1 {
		gameData.HRate = gameNewRate
		otherGameData.ARate = otherGameNewRate
	} else {
		gameData.ARate = gameNewRate
		otherGameData.HRate = otherGameNewRate
	}

	if err := models.UpsertGameData(gameData); err != nil {
		logs.Warn("平台 %d 保存水位失败 %s", gameData.PlatformId, err.Error())
	}

	if err := models.UpsertGameData(otherGameData); err != nil {
		logs.Warn("平台 %d 保存水位失败 %s", otherGameData.PlatformId, err.Error())
	}

	// 避免重复下单
	uniqKey := fmt.Sprintf("%d_%d", gameData.PlatformId, gameDataTeam)
	if gameData.PlatformId > otherGameData.PlatformId {
		uniqKey = fmt.Sprintf("%d_%d", otherGameData.PlatformId, otherGameDataTeam)
	}

	key := fmt.Sprintf("%s:%d_%0.2f:%d_%0.2f", uniqKey, gameData.GameId, gameData.Handicap, otherGameData.GameId, otherGameData.Handicap)
	if value, err := u.cache.GetSet(key, "ok"); err == nil && value != "" {
		logs.Warn("最近%d秒下过单，下单失败", u.cache.DefualtExp()/time.Second)
		return
	}

	u.cache.Set(key, "ok")
	logs.Notice("================================准备下单啦")

	order := &models.Order{
		UserId:   u.user.Id,
		PolicyId: policy.Id,
		HScore:   gameData.HScore,
		AScore:   gameData.AScore,

		Platform1Id:           gameData.PlatformId,
		Platform1AgentId:      gameLottery.AgentInfo().Id,
		Platform1GameId:       gameData.GameId,
		Platform1GameDataId:   gameData.Id,
		Platform1Rate:         gameNewRate,
		Platform1Team:         gameDataTeam,
		Platform1Handicap:     gameData.Handicap,
		Platform1HandicapType: gameData.HandicapType,

		Platform2Id:           otherGameData.PlatformId,
		Platform2AgentId:      otherGameLottery.AgentInfo().Id,
		Platform2GameId:       otherGameData.GameId,
		Platform2GameDataId:   otherGameData.Id,
		Platform2Rate:         otherGameNewRate,
		Platform2Team:         otherGameDataTeam,
		Platform2Handicap:     otherGameData.Handicap,
		Platform2HandicapType: otherGameData.HandicapType,
		CreateTime:            time.Now().UnixNano() / int64(time.Millisecond),
	}

	if err := models.SaveOrder(order); err != nil {
		u.cache.Del(key)
		logs.Warn("save order: %+v 失败", order)
		return
	}

	if nowMergeRate < 0 {
		order.UpdatePlatform1PlayFail("调试下单")
		return
	}

	gamePrice, ok := u.getBasePrice(policy.Price, gameData, otherGameData)
	if !ok {
		order.UpdatePlatform1PlayFail("价格过低，不下注")
		u.cache.Del(key)
		logs.Warn("order:%d policy:%d 平台:%d agentId:%d gameData:%d 下注失败 %s", order.Id, policy.Id, gameData.PlatformId, gameLottery.AgentInfo().Id, gameData.Id, "价格过低，不下注")
		return
	}

	// 试水不能超过25块
	if otherGameData.LowPrice > 2500 {
		order.UpdatePlatform1PlayFail("试水价格过高，不下注")
		u.cache.Del(key)
		logs.Warn("order:%d policy:%d 平台:%d agentId:%d gameData:%d 下注失败 %s", order.Id, policy.Id, gameData.PlatformId, gameLottery.AgentInfo().Id, gameData.Id, "试水价格过高，不下注")
		return
	}

	// 试水
	logs.Notice("order:%d 开始试水时长:%d", order.Id, utils.CurrentMills()-startProcTime)
	if _, _, err = u.playOrder(otherGameLottery, otherGameData.LowPrice, otherGameDataTeam, otherGameTickData, otherGameData, false); err != nil {
		logs.Warn("平台 %d 试投失败 无法下单 %d err:%s", otherGameLottery.Platform(), otherGameDataTeam, err.Error())
		order.UpdatePlatform1PlayFail(fmt.Sprintf("试水失败:%s", err.Error()))
		u.cache.Del(key)
		return
	}
	logs.Notice("order:%d 结束试水时长:%d", order.Id, utils.CurrentMills()-startProcTime)

	//////////////////

	// 第一个站点下单
	logs.Notice("order:%d 开始第一个站点下单时长:%d", order.Id, utils.CurrentMills()-startProcTime)
	try, newGameTickData, err := u.playOrder(gameLottery, gamePrice, gameDataTeam, gameTickData, gameData, false)
	if err != nil && try && gameLottery.Platform() == constant.U16888 {
		// 下单重试
		logs.Notice("policy:%d 平台:%d agentId:%d order:%d rate:%0.2f 重试下单",
			policy.Id, gameLottery.Platform(), gameLottery.AgentInfo().Id, order.Id, gameNewRate)
		_, _, err = u.playOrder(gameLottery, gamePrice, gameDataTeam, newGameTickData, gameData, false)
	}
	logs.Notice("order:%d 完成第一个站点下单时长:%d", order.Id, utils.CurrentMills()-startProcTime)

	if err != nil {
		order.UpdatePlatform1PlayFail(err.Error())
		u.cache.Del(key)
		logs.Warn("order:%d policy:%d 平台:%d agentId:%d gameData:%d 下注失败 %s", order.Id, policy.Id, gameData.PlatformId, gameLottery.AgentInfo().Id, gameData.Id, err.Error())
	} else {
		logs.Notice("order:%d 平台:%d agentId:%d 下注成功:%+v", order.Id, gameData.PlatformId, gameLottery.AgentInfo().Id, gameData)
		if err := order.UpdatePlatform1Status(gamePrice, gameLottery.AgentInfo().Id); err != nil {
			logs.Warn("UpdatePlatform1Status order:%d err:%s", order.Id, err.Error())
		}
		logs.Notice("order:%d 完成第一个站点数据存储时长:%d", order.Id, utils.CurrentMills()-startProcTime)

		// 第二个站点下单
		otherGamePrice := u.cpuPlayPrice(order.Platform1Rate, order.Platform2Rate, gamePrice)
		tryTimes := 0
		try, newOtherGameTickData, err := u.playOrder(otherGameLottery, otherGamePrice, otherGameDataTeam, otherGameTickData, otherGameData, false)
		if err != nil {
			logs.Notice("order:%d 完成第二个站点下单时长:%d", order.Id, utils.CurrentMills()-startProcTime)
			order.UpdatePlatform2PlayFail(err.Error())
			if try {
				if otherGameDataTeam == 1 {
					otherGameNewRate = otherGameData.HRate
				} else {
					otherGameNewRate = otherGameData.ARate
				}

				// 补单
				tryTimes++
				logs.Notice("policy:%d 平台:%d agentId:%d order:%d rate:%0.2f 重试补单",
					policy.Id, otherGameLottery.Platform(), otherGameLottery.AgentInfo().Id, order.Id, otherGameNewRate)
				otherGamePrice = u.cpuPlayPrice(order.Platform1Rate, otherGameNewRate, gamePrice)
				_, _, err = u.playOrder(otherGameLottery, otherGamePrice, otherGameDataTeam, newOtherGameTickData, otherGameData, false)
			}
		} else {
			logs.Notice("order:%d 完成第二个站点下单时长:%d", order.Id, utils.CurrentMills()-startProcTime)
		}

		if err == nil {
			if !try {
				// 首单更新结果
				order.UpdatePlatform2Status(otherGameLottery.AgentInfo().Id, otherGameNewRate, otherGamePrice, otherGameData)
			} else {
				// 补单更新结果
				order.UpdatePlatform2Status2(otherGameLottery.AgentInfo().Id, otherGameNewRate, tryTimes, otherGamePrice, otherGameData)
			}
			logs.Notice("order:%d platformId:%d agentId:%d 下注成功:%+v", order.Id, otherGameData.PlatformId, otherGameLottery.AgentInfo().Id, otherGameData)
			u.orderChan <- order
			return
		}

		// 下单失败
		// 补单
		logs.Warn("policy:%d order:%d platformId:%d agentId:%d gameData:%d 下注失败 %s", policy.Id, order.Id, otherGameData.PlatformId, otherGameLottery.AgentInfo().Id, otherGameData.Id, err.Error())
		go func(order *models.Order, policy *models.Policy, otherGameDataTeam int, otherGameData *models.GameData, tryTimes int, gamePrice int) {
			platformId := otherGameData.PlatformId
			gameId := otherGameData.GameId
			handicapType := otherGameData.HandicapType
			handicap := otherGameData.Handicap
			var (
				otherGameNewRate  float64
				otherGameTickData interface{}
				switchIP          bool = true
			)

			game := otherGameData.Game
			if game != nil {
				logs.Notice("补单比赛 联赛名:%s %s:%s", game.LeagueGameName, game.HName, game.AName)
			}

			// testUp := true
			ctx, cancel := context.WithTimeout(context.Background(), time.Minute*10)
			defer cancel()
			for {
				select {
				case <-ctx.Done():
					msg := fmt.Sprintf("platformId:%d order:%d team:%d 下注失败 超时", platformId, order.Id, otherGameDataTeam)
					logs.Warn(msg)
					order.UpdatePlatform2PlayFail2(msg)
					u.orderChan <- order
					return
				default:
					logs.Debug("获取最新水位")
					otherGameData := u.getGameData(platformId, gameId, handicapType, handicap)
					if otherGameData == nil {
						logs.Warn("platformId:%d 获取水位失败 盘口消失 order:%d team:%d handicapType:%d handicap:%0.2f 休眠100ms",
							platformId,
							order.Id,
							otherGameDataTeam,
							handicapType,
							handicap,
						)
						time.Sleep(time.Millisecond * 100)
						continue
					}

					if otherGameData.HScore != order.HScore || otherGameData.AScore != order.AScore {
						msg := fmt.Sprintf("platformId:%d order:%d team:%d 比分改变，不再下注 原比分 %d:%d  现比分 %d:%d",
							platformId,
							order.Id,
							otherGameDataTeam,
							order.HScore,
							order.AScore,
							otherGameData.HScore,
							otherGameData.AScore,
						)
						logs.Warn(msg)
						order.UpdatePlatform2PlayFail2(msg)
						return
					}

					lottery, ok := u.getPlaceLottery(otherGameData.GetAgentId(), platformId)
					if !ok {
						// 休眠xx毫秒
						logs.Warn("platformId:%d order:%d 获取不到下单账号 无法下单", platformId, order.Id)
						time.Sleep(time.Millisecond * 100)
						continue
					}

					if otherGameData.PlatformId == constant.U16888 {
						if !u.fillU16888HandicapId(lottery.AgentInfo(), otherGameData) {
							logs.Warn("平台 %d 获取打水账号 %d, 填充下注ID失败", otherGameData.PlatformId, lottery.AgentInfo().Id)
							u.putPlaceLottery(lottery)
							continue
						}
					}
					otherGameNewRate, otherGameTickData, _, err = lottery.Tick(otherGameDataTeam, otherGameData)
					if err != nil {
						logs.Warn("平台 %d 获取点水失败 无法下单 %d %+v %s", platformId, otherGameDataTeam, otherGameData, err.Error())
						u.putPlaceLottery(lottery)
						continue
					}

					// 再次确认比分
					if otherGameData.HScore != order.HScore || otherGameData.AScore != order.AScore {
						msg := fmt.Sprintf("platformId:%d order:%d team:%d 比分改变，不再下注 原比分 %d:%d  现比分 %d:%d",
							platformId,
							order.Id,
							otherGameDataTeam,
							order.HScore,
							order.AScore,
							otherGameData.HScore,
							otherGameData.AScore,
						)
						logs.Warn(msg)
						order.UpdatePlatform2PlayFail2(msg)
						u.putPlaceLottery(lottery)
						return
					}

					if otherGameDataTeam == 1 {
						otherGameData.HRate = otherGameNewRate
					} else {
						otherGameData.ARate = otherGameNewRate
					}

					tryTimes++
					// 计算下单价格
					otherGamePrice := u.cpuPlayPrice(order.Platform1Rate, otherGameNewRate, gamePrice)
					try, newOtherGameTickData, err := u.playOrder(lottery, otherGamePrice, otherGameDataTeam, otherGameTickData, otherGameData, switchIP)
					if try && err != nil {
						// 二次补单
						if otherGameDataTeam == 1 {
							otherGameNewRate = otherGameData.HRate
						} else {
							otherGameNewRate = otherGameData.ARate
						}
						tryTimes++
						logs.Notice("policy:%d 平台:%d agentId:%d order:%d rate:%0.2f 重试补单",
							policy.Id, lottery.Platform(), lottery.AgentInfo().Id, order.Id, otherGameNewRate)
						// 计算下单价格
						otherGamePrice = u.cpuPlayPrice(order.Platform1Rate, otherGameNewRate, gamePrice)
						_, _, err = u.playOrder(lottery, otherGamePrice, otherGameDataTeam, newOtherGameTickData, otherGameData, switchIP)
					}

					if err != nil {
						msg := err.Error()
						logs.Warn("policy:%d order:%d 平台:%d agentId:%d gameData:%d rate:%0.2f 下注失败 %s", policy.Id, order.Id, platformId, lottery.AgentInfo().Id, otherGameData.Id, otherGameNewRate, msg)
						u.putPlaceLottery(lottery)
					} else {
						order.UpdatePlatform2Status2(lottery.AgentInfo().Id, otherGameNewRate, tryTimes, otherGamePrice, otherGameData)
						logs.Notice("order:%d platformId:%d agentId:%d 补单水位 %0.2f 下注成功:%+v", order.Id, platformId, lottery.AgentInfo().Id, otherGameNewRate, otherGameData)
						u.putPlaceLottery(lottery)
						u.orderChan <- order
						return
					}
				}
			}
		}(order, policy, otherGameDataTeam, otherGameData, tryTimes, gamePrice)
	}
	return
}

// team 1 主场 2 客场
func (u *UserProc) playOrder(lottery ILottery, price int, team int, tickData interface{}, gameData *models.GameData, switchIP bool) (try bool, newTickData interface{}, err error) {
	actionResp := lottery.Place(price, team, tickData, gameData, switchIP)
	if actionResp.ExtData != nil {
		switch lottery.Platform() {
		case constant.ISN99:
			// 更新盘口数据
			if actionResp.ExtData != nil {
				try = true
				newTickData = actionResp.ExtData
			}
		case constant.U16888:
			try, _ = actionResp.ExtData.(bool)
		}
	}

	if !actionResp.IsSuccess {
		err = errors.New(actionResp.Msg)
		return
	}

	return
}

func (u *UserProc) getBasePrice(policyPrice int, gameData *models.GameData, otherGameData *models.GameData) (price int, ok bool) {
	if policyPrice < gameData.LowPrice && policyPrice < otherGameData.LowPrice {
		return
	}

	// 取最小金额
	price = int(float64(gameData.HighPrice) * 0.85)
	otherGameDataPriceHigh := int(float64(otherGameData.HighPrice) * 0.85)
	if otherGameDataPriceHigh < price {
		price = otherGameDataPriceHigh
	}

	// 取最小金额
	if policyPrice < price {
		price = policyPrice
	}

	if price < gameData.LowPrice || price < otherGameData.LowPrice {
		return
	}

	ok = true
	return
}

// 输入先下注赔率，后下注赔率，先下注金额
// 返回后下注应下注金额,是否可下注
func (u *UserProc) cpuPlayPrice(rate1 float64, rate2 float64, rate1Price int) (rate2Price int) {
	rate2Price = int((1 + math.Abs(rate1)) / (1 + math.Abs(rate2)) * float64(rate1Price))
	return
}
